﻿using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Text;
using System.Windows.Forms;
using AndBurn.DDSReader;
using DnPakExplorer.Forms;
using Vlc.DotNet.Core.Interops;
using Vlc.DotNet.Core;
using Vlc.DotNet.Forms;
using System.Xml;
using ZLibNet;

namespace DnPakExplorer {
	public partial class DnPakExplorer : Form {

		const int ICON_INDEX_FONT = 0;
		const int ICON_INDEX_DNT = 1;
		const int ICON_INDEX_ERROR = 2;
		const int ICON_INDEX_LUO = 3;
		const int ICON_INDEX_FOLDER = 8;
		const int ICON_INDEX_FOLDER_CLOSED = 16;
		const int ICON_INDEX_TEXT = 5;

		FormProgress mProgress;

		public DnPakExplorer() {
			InitializeComponent();
		}
			


		void ReadPak(string filename, List<FileHeader> headers) {
			FileStream file = new FileStream(filename, FileMode.Open);
			BinaryReader br = new BinaryReader(new BufferedStream(file));

			byte[] fileID = br.ReadBytes(256);
			int ver = br.ReadInt32();
			int recordCount = br.ReadInt32();
			int firstRecordHeadOffset = br.ReadInt32();
			byte[] reserved = br.ReadBytes(756);

			br.BaseStream.Seek(firstRecordHeadOffset, SeekOrigin.Begin);
			byte[] buff = new byte[256];
			int fileCount = 0;
			for (int i=0; i<recordCount; i++) {
				FileHeader header = new FileHeader();
				br.Read(buff, 0, 256);
				int len;
				for (len=0; len<256; len++)
					if (buff[len]==0)
						break;
				header.fileName = System.Text.Encoding.Default.GetString(buff, 0, len);
				header.packedSize = br.ReadInt32();
				header.realSize = br.ReadInt32();
				header.sizeInPak = br.ReadInt32();
				header.pakOffset = br.ReadInt32();
				header.flag = br.ReadInt32();
				header.reserved = br.ReadBytes(40);
				header.pakFilePath = filename;
				if (header.fileName == "") {
					// TODO
					Console.Write("Warning: empty filename inf pak file("+header.pakFilePath+") founded");
					header.fileName = "Empty named file.unkown";
				}
				//if(header.fileName.EndsWith(".mDnt"))
				headers.Add(header);
				fileCount++;
				if (fileCount%100==0) {
					//Thread.Sleep(10);
					//Application.DoEvents();
				}
			}
			curCount++;
			total += fileCount;
			progress = (double)curCount/pakCount;
			br.Close();
			file.Close();

		}

		double progress = 0;
		int curCount = 0;
		int pakCount = 0;
		int step = 0;
		int added = 0;
		int total = 0;

		private void OnLoad(object sender, EventArgs e) {
			if (MessageBox.Show("是否导入龙之谷资源包？", "提示", MessageBoxButtons.OKCancel) == System.Windows.Forms.DialogResult.OK) {
				LoadDnResources();
			}
		}

		private void LoadDnResources() {
			DnPakFileSelectForm form = new DnPakFileSelectForm();
			if (form.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
				Thread thead = new Thread(delegate() {
					mProgress = new FormProgress();
					mProgress.StartPosition = FormStartPosition.CenterScreen;
					mProgress.Show();
					while (step<2) {
						if(step == 0)
							mProgress.SetProgressBarPosition((int)(progress*1000));
						else
							mProgress.SetProgressBarPosition((int)(added*1000/total));
						mProgress.SetInfomation(step==0?"读取数据。。。":"处理数据。。。");
						Application.DoEvents();
						System.Threading.Thread.Sleep(10);
					}
					mProgress.Close();
				});
				thead.Start();

				string[] paths = form.SelectedFiles;
				pakCount = paths.Length;
				curCount = 0;
				progress = 0;
				added = 0;
				total = 0;
				step = 0;
				List<FileHeader> headers = new List<FileHeader>(1000);
				foreach (string path in paths) {
					ReadPak(path, headers);
				}
				LoadData("..\\..\\..\\Resources\\Data.xml");
				step = 1;
				TreeNode filesNode = new TreeNode("Files");
				filesNode.ImageIndex = ICON_INDEX_FOLDER_CLOSED;
				Add(filesNode.Nodes, 1, headers);
				// 先构建子树再添加到视图中会极大提升速度
				treeView1.Nodes.Add(filesNode);
				step = 2;
			}	
		}

		private void LoadData(string filepath) {
			XmlDocument doc = new XmlDocument();
			doc.Load(filepath);
			XmlNodeList entryNodeList = doc.SelectNodes("/Root/PakFile/CommonPakEntries/Entry");
			TreeNode entries = treeView1.Nodes.Add("Entries");
			foreach (XmlNode entryNode in entryNodeList) {
				TreeNode entry = entries.Nodes.Add(entryNode.Attributes.GetNamedItem("name").InnerText);
				entry.Tag = entryNode.Attributes.GetNamedItem("path");
				entry.ImageIndex = GetFileIconInde("dnt");
			}
		}

		private void Add(TreeNodeCollection parent, int offset, List<FileHeader> headers) {
			Hashtable directories = new Hashtable();
			List<FileHeader> files = new List<FileHeader>();
			for (int i=0; i<headers.Count; i++) {
				int index = headers[i].fileName.IndexOf('\\', offset);
				if (index >= 0) {
					string name = headers[i].fileName.Substring(offset, index-offset);
					List<FileHeader> list = directories[name] as List<FileHeader>;
					if (list == null) {
						list = new List<FileHeader>();
						directories.Add(name, list);
					}
					list.Add(headers[i]);
				} else {
					files.Add(headers[i]);
				}
			}
			files.Sort();
			//TreeNode[] fileNodes = new TreeNode[files.Count];
			//for (int i=0; i<fileNodes.Length; i++) {
			//	fileNodes[i] = new TreeNode(files[i].fileName);
			//	fileNodes[i].Tag = files[i];
			//	fileNodes[i].ImageIndex = GetFileIconInde(files[i].fileName);
			//	added++;
			//}
			//parent.AddRange(fileNodes);
			foreach (FileHeader header in files) {
				TreeNode node = parent.Add(header.fileName.Substring(offset));
				node.Tag = header;
				node.ImageIndex = GetFileIconInde(header.fileName);
				added++;
			}
			DictionaryEntry[] arr = new DictionaryEntry[directories.Count];
			var items = directories.Values;
			var keys = directories.Keys;
			directories.CopyTo(arr, 0);
			Array.Sort(arr, new a());
			foreach (DictionaryEntry entry in arr) {
				TreeNode node = parent.Add(entry.Key.ToString());
				node.ImageIndex = ICON_INDEX_FOLDER_CLOSED;
				Add(node.Nodes, offset+entry.Key.ToString().Length+1, (List<FileHeader>)entry.Value);
			}
		}

		class a : IComparer {
			public int Compare(object x, object y) {
				return ((string)((DictionaryEntry)x).Key).CompareTo((string)((DictionaryEntry)y).Key);
			}
		}

		private int GetFileIconInde(string filename) {
			switch (Util.GetFileNameExt(filename).ToLower()) {
			case "dnt":
				return 18;
			case "dds":
				return 12;
			case "font":
				return 9;
			case "lua":
				return 6;
			case "xml":
				return 19;
			case "avi":
				return 17;
			case "ogg":
			case "wav":
			case "mp3":
				return 20;
			case "msh":
				return 21;
			case "eff":
				return 22;
			case "jpg":
			case "png":
			case "gif":
				return 10;
			default:
				return 15;
			}
		}

		private void OnMenuOpen(object sender, EventArgs e) {
			OpenFileDialog dlg = new OpenFileDialog();
			dlg.Filter = "*dnt|*.dnt|*.pak|*.pak";
			dlg.CheckFileExists = true;
			dlg.Multiselect = false;
			if (dlg.ShowDialog() == DialogResult.OK) {
				if (dlg.FileName.EndsWith(".dnt", StringComparison.OrdinalIgnoreCase)) {
					DntExplorer dnt = new DntExplorer();
					dnt.LoadDnt(dlg.FileName);
					dnt.Show();
				} else if (dlg.FileName.EndsWith(".pak", StringComparison.OrdinalIgnoreCase)) {
					List<FileHeader> headers = new List<FileHeader>(1000);
					ReadPak(dlg.FileName, headers);
					treeView1.Nodes.Clear();
					Add(treeView1.Nodes, 1, headers);
				} 
			}
		}


		private void OnContextMenu(object sender, EventArgs e) {
			ToolStripItem item = (ToolStripItem)sender;
			if (item.Text == "提取") {
				TreeNode node = item.Tag as TreeNode;
				FileHeader header = node.Tag as FileHeader;

				SaveFileDialog dlg = new SaveFileDialog();
				dlg.FileName = Util.GetFileName(header.fileName);
				string ext = "*."+Util.GetFileNameExt(header.fileName).ToLower();
				if (ext.Equals("*.dds", StringComparison.OrdinalIgnoreCase)) {
					dlg.Filter = "*.bmp|*.bmp|*.dds|*.dds|*.emf|*.emf|*.exif|*.exif|*.gif|*.gif|*.ico|*.ico|*.jpg|*.jpg|*.png|*.png|*.tiff|*.tiff|*.wmf|*.wmf";
				} else {
					dlg.Filter = ext+"|"+ext;
				}
				
				if (dlg.ShowDialog() != System.Windows.Forms.DialogResult.OK)
					return;

				string targetExt = "*."+Util.GetFileNameExt(dlg.FileName).ToLower();
				if (targetExt == ext) {
					FileStream ofs = new FileStream(dlg.FileName, FileMode.Create);
					Stream ins = GetFileStream(header);
					byte[] buffer = new byte[10240];
					int lenRead = 0;
					while ((lenRead=ins.Read(buffer, 0, buffer.Length)) > 0) {
						ofs.Write(buffer, 0, lenRead);
					}
					ins.Close();
					ofs.Close();
				} else if (ext == "*.dds") {
					ImageFormat imageFormat = null;
					switch (targetExt) {
					case "*.bmp":
						imageFormat = ImageFormat.Bmp;
						break;
					case "*.emf":
						imageFormat = ImageFormat.Emf;
						break;
					case "*.exif":
						imageFormat = ImageFormat.Exif;
						break;
					case "*.gif":
						imageFormat = ImageFormat.Gif;
						break;
					case "*.icon":
						imageFormat = ImageFormat.Icon;
						break;
					case "*.jpeg":
						imageFormat = ImageFormat.Jpeg;
						break;
					case "*.png":
						imageFormat = ImageFormat.Png;
						break;
					case "*.tiff":
						imageFormat = ImageFormat.Tiff;
						break;
					case "*.wmf":
						imageFormat = ImageFormat.Wmf;
						break;
					default:
						MessageBox.Show("不支持的文件格式");
						break;
					}

					if (imageFormat!= null) {
						using (Stream ins = GetFileStream(header)) {
							DDSImage dds = new DDSImage(ins);
							dds.BitmapImage.Save(dlg.FileName, imageFormat);
						}
					}
				}

				
			} else if (item.Text == "属性") {
				TreeNode node = item.Tag as TreeNode;
				FileHeader header = node.Tag as FileHeader;
				if (header != null) {
					WebMessage msg = new WebMessage();
					msg.Text = "属性";
					msg.SetHtmlString(new StringBuilder()
						.Append(@"<html><body style=""font:normal 12px/24px Helvetica, Tahoma, Arial, sans-serif""><li>文件名:").Append(header.fileName)
						.Append("<il><li>压缩大小:").Append(header.packedSize)
						.Append("<il><li>实际大小:").Append(header.realSize)
						.Append("<il><li>包内大小:").Append(header.sizeInPak)
						.Append("<il><li>包内偏移:").Append(header.pakOffset)
						.Append("<il><li>标志:").Append(header.flag)
						.Append("<il><li>所属PAK文件:").Append(header.pakFilePath)
						.Append("<il></body></html>").ToString());
					msg.Size = new Size(500, 260);
						
					msg.StartPosition = FormStartPosition.CenterParent;
					msg.Show();
				}
			} else if (item.Text == "复制名称") {
				TreeNode node = item.Tag as TreeNode;
				FileHeader header = node.Tag as FileHeader;
				Clipboard.SetDataObject(Util.GetFileName(header.fileName), true);
			} else if (item.Text == "复制路径") {
				TreeNode node = item.Tag as TreeNode;
				FileHeader header = node.Tag as FileHeader;
				Clipboard.SetDataObject(header.fileName, true);
			}
		}

		private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) {
			if (e.Button == MouseButtons.Right) {
				ContextMenuStrip menu = new ContextMenuStrip();
				if (e.Node.Nodes.Count==0)
					menu.Items.Add("提取", null, OnContextMenu).Tag = e.Node;
				menu.Items.Add("属性", null, OnContextMenu).Tag = e.Node;
				menu.Items.Add("复制名称", null, OnContextMenu).Tag = e.Node;
				menu.Items.Add("复制路径", null, OnContextMenu).Tag = e.Node;
				menu.Show(Cursor.Position);
			}
		}

		private void OnMenuEquip(object sender, EventArgs e) {
			new Equiper().Show();
		}

		private Stream GetFileStream(FileHeader header) {
			FileStream fs = new FileStream(header.pakFilePath, FileMode.Open);
			fs.Seek(header.pakOffset, SeekOrigin.Begin);
			ZLibStream z = new ZLibStream(fs, ZLibNet.CompressionMode.Decompress);
			return new BufferedStream(z);
		}

		private Stream GetRawFileStream(FileHeader header) {
			FileStream fs = new FileStream(header.pakFilePath, FileMode.Open);
			fs.Seek(header.pakOffset, SeekOrigin.Begin);
			return new BufferedStream(fs);
		}

		private void treeView1_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e) {
			if (e.Node.Nodes.Count == 0) {
				FileHeader header = e.Node.Tag as FileHeader;
				if (header != null) {
					if (header.fileName.EndsWith(".dnt", StringComparison.OrdinalIgnoreCase)) {
						using(Stream s = GetFileStream(header)){
							DntExplorer dnt = new DntExplorer();
							dnt.LoadDnt(s);
							dnt.StartPosition = FormStartPosition.CenterScreen; 
							dnt.Show();
						}
					}

					if (header.fileName.EndsWith(".dds", StringComparison.OrdinalIgnoreCase)) {
						using (Stream s = GetFileStream(header)) {
							DDSForm dds = new DDSForm();
							dds.Text = Util.GetFileName(header.fileName);
							dds.LoadDDS(s);
							dds.StartPosition = FormStartPosition.CenterScreen;
							dds.Show();
						}
					}
					if (header.fileName.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase)
						||header.fileName.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)
						||header.fileName.EndsWith(".png", StringComparison.OrdinalIgnoreCase)) {
						using (Stream s = GetFileStream(header)) {
							DDSForm dds = new DDSForm();
							dds.Text = Util.GetFileName(header.fileName);
							dds.StartPosition = FormStartPosition.CenterScreen;
							dds.SetImage(new Bitmap(s));
							dds.Show();
						}
					}

					if (header.fileName.EndsWith(".wav", StringComparison.OrdinalIgnoreCase)
						|| header.fileName.EndsWith(".ogg", StringComparison.OrdinalIgnoreCase)) {
						using (Stream s = GetFileStream(header)) {
						}
					}
				}
				string path = e.Node.Tag as string;
				if (path != null) {

				}
			}
		}

		public FileHeader FindFileHeaderWithPath(string path) {
			TreeNode[] roots = treeView1.Nodes.Find("Files", false);
			if (roots == null || roots.Length<=0)
				return null;
			int start = path.StartsWith("\\")?1:0;
			int len = path.Length;
			do {
				int index = path.IndexOf('\\');
				string name = null;
				if (index < 0) {
					name = path.Substring(start);
					start = len;
				} else {
					name = path.Substring(start, index);
					start = index+1;
				}

			} while (start<len);
		}


		private void OnMenuLoadDnResources(object sender, EventArgs e) {
			LoadDnResources();
		}

		private void treeView1_AfterExpand(object sender, TreeViewEventArgs e) {
			e.Node.ImageIndex = ICON_INDEX_FOLDER;
			if (e.Node.IsSelected) {
				e.Node.SelectedImageIndex = e.Node.ImageIndex;
			}
		}

		private void treeView1_AfterCollapse(object sender, TreeViewEventArgs e) {
			e.Node.ImageIndex = ICON_INDEX_FOLDER_CLOSED;
			if (e.Node.IsSelected) {
				e.Node.SelectedImageIndex = e.Node.ImageIndex;
			}
		}

		private void treeView1_AfterSelect(object sender, TreeViewEventArgs e) {
			e.Node.SelectedImageIndex = e.Node.ImageIndex;
		}

		private void treeView1_BeforeSelect(object sender, TreeViewCancelEventArgs e) {
			//e.Node.SelectedImageIndex = e.Node.ImageIndex;
		}

		private void DnPakExplorer_FormClosed(object sender, FormClosedEventArgs e) {
			treeView1.Nodes.Clear();
		}

		private void TestOne(object sender, EventArgs e) {
			new VideoForm().Show();
		}

		private void TestTwo(object sender, EventArgs e) {
			new Vlc.DotNet.Forms.Samples.Sample().Show();
		}
	}

	class FileHeader : IComparable<FileHeader> {
		public string fileName;	// 占256字节，ASCII文本，CodePage=949
		public Int32 packedSize;
		public Int32 realSize;
		public Int32 sizeInPak;
		public Int32 pakOffset;
		public Int32 flag;
		public byte[] reserved; // 40 bytes;
		public string pakFilePath;

		public int CompareTo(FileHeader other) {
			return fileName.CompareTo(other.fileName);
		}
	}

	public class UIString {
		public int id;
		public string text;
	}
}
